package com.spdzen.support.shiro;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.Ini.Section;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;

import com.spdzen.model.SysResource;
import com.spdzen.model.SysRoleResource;
import com.spdzen.service.SysResourceService;
import com.spdzen.service.SysRoleResourceService;
import com.spdzen.support.constant.DbConstants;

public class SimpleShiroFilterFactoryBean extends ShiroFilterFactoryBean {
	private static final String EMPTY_DEFINITIONS = "/**=anon";
	
	private String defaultFilterChainDefinitions = "";
	
	@Resource
	private SysRoleResourceService sysRoleResourceService;

	@Resource
	private SysResourceService sysResourceService;
	
	private Lock lock = new ReentrantLock();
	
	@Override
	public void setFilterChainDefinitions(String definitions) {
		this.defaultFilterChainDefinitions = definitions;		
        setFilterChainDefinitionMap(getFilterChainDefinitions());
    }
	
	public void refreshFilterChainDefinitions() throws Exception {
		try {
			lock.lock();
			
			AbstractShiroFilter shiroFilter = (AbstractShiroFilter) this.getObject();
			PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
			// 过滤管理器
	        DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();
	        
	        // 清除权限配置
	        manager.getFilterChains().clear();
	        
	        this.setFilterChainDefinitionMap(getFilterChainDefinitions());
	        
	        Map<String, String> chains = this.getFilterChainDefinitionMap();
	        if(chains != null && chains.size() > 0) {
	        	chains.forEach((url, chainDefinition) -> {
	        		manager.createChain(url, chainDefinition);
	        	});
	        }
		} finally {
			lock.unlock();
		}
	}
	
	private Section getFilterChainDefinitions() {
		Ini ini = new Ini();
		ini.load(EMPTY_DEFINITIONS);
		Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
		section.clear();

		List<SysResource> resources = sysResourceService.selectAll();
		List<SysResource> avails = new LinkedList<>();
		List<String> urls = new LinkedList<>();
		for (SysResource item : resources) {
			String url = item.getUrl();
			int type = item.getType().intValue();
			if (type != DbConstants.SYS_RESOURCE_TYPE_DIR && StringUtils.isNotBlank(url)) {
				urls.add(url);
				avails.add(item);
			}
		}

		if (urls.size() > 0) {
			List<SysRoleResource> srs = sysRoleResourceService.selectAll();
			Map<Long, List<Long>> srmap = new HashMap<Long, List<Long>>();
			for (SysRoleResource item : srs) {
				List<Long> rs = srmap.get(item.getSysResourceId());
				if (rs == null) {
					rs = new LinkedList<Long>();
					srmap.put(item.getSysResourceId(), rs);
				}

				rs.add(item.getSysRoleId());
			}

			StringBuilder sb = null;
			for (SysResource item : avails) {
				Long sysResourceId = item.getId();

				sb = new StringBuilder("roles[\"");
				
				// 超级用户和隐藏用户拥有所有普通资源和系统默认资源的权限
				sb.append("-2");

				List<Long> roleIds = srmap.get(sysResourceId);
				if (roleIds == null || roleIds.size() == 0) {
					sb.append("\"]");
				} else if (roleIds.size() == 1) {
					sb.append(",").append(roleIds.get(0).toString()).append("\"]");
				} else {
					sb.append(",").append(StringUtils.join(roleIds, ",")).append("\"]");
				}

				section.put(item.getUrl(), sb.toString());
			}
		}
		
		Ini.Section defaultSection = getDefaultFilterChainDefinitions();
		if (defaultSection != null && defaultSection.size() > 0) {
			section.putAll(defaultSection);
		}
		
		return section;
	}
	
	private Section getDefaultFilterChainDefinitions() {
		Ini ini = new Ini();
		ini.load(this.defaultFilterChainDefinitions);
		Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
		return section;
	}
}
